package com.fingard.dsp.bank.directbank.crbc01.util;

import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.*;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;


public class CrbankDES {

    /**
     * 加密密钥
     */
    private String PRIVATE_CRYPT_KEY="";

    /**
     * 矢量
     */
    private String PRIVATE_IV="zhcrbank";

    /**
     * 填充模式
     */
    private String CIPHER_MODEL="DESede/CBC/PKCS5Padding";

    /**
     * 算法类型
     */
    private  static String ALGORITHM="DESede";


    private static CrbankDES crbankDES=new CrbankDES();


    public static CrbankDES getInstance(String privateCryptKey){
        crbankDES.PRIVATE_CRYPT_KEY = privateCryptKey;
        return crbankDES;
    }



    public void setPRIVATE_CRYPT_KEY(String pRIVATE_CRYPT_KEY) {
        PRIVATE_CRYPT_KEY = pRIVATE_CRYPT_KEY;
    }



    public void setPRIVATE_IV(String pRIVATE_IV) {
        PRIVATE_IV = pRIVATE_IV;
    }



    public void setCIPHER_MODEL(String cIPHER_MODEL) {
        CIPHER_MODEL = cIPHER_MODEL;
    }



    public void setALGORITHM(String aLGORITHM) {
        ALGORITHM = aLGORITHM;
    }

    /**
     * 加解密字符串
     * @param encrptyStr 需要加解密字符串
     * @param charset 为空是，使用系统默认编码
     * @param mode  加密或解密模式
     * @return
     */
    public String encrptyString(String encrptyStr, String charset, int mode){
        try{
            //得到加密模式
            Cipher cipher=getCipher(mode);
            if(mode== Cipher.ENCRYPT_MODE){
                //加密内容
                if(charset==null){
                    return new BigInteger(cipher.doFinal(encrptyStr.getBytes())).toString(16);
                }else{
                    return new BigInteger(cipher.doFinal(encrptyStr.getBytes(charset))).toString(16);
                }
            }else if(mode== Cipher.DECRYPT_MODE){
                //解密内容
                if(charset==null){
                    return new String(cipher.doFinal(new BigInteger(encrptyStr,16).toByteArray()));
                }else{
                    return new String(cipher.doFinal(new BigInteger(encrptyStr,16).toByteArray()),charset);
                }
            }else{
                return encrptyStr;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return encrptyStr;
    }


    /**
     * 文件加解密
     * @param encrptyFilePath 加解密文件
     * @param encrptyedFilePath 加解密后的文件
     * @param mode 加密或解密模式
     * @param bufferSize 加解密缓冲大小，若bufferSize<=0,则为默认值：1024*1024
     */
    public  void encrptyFile(String encrptyFilePath, String encrptyedFilePath, int mode, int bufferSize){
        encrptyFile(new File(encrptyFilePath), new File(encrptyedFilePath), mode, bufferSize);
    }


    /**
     * 文件加解密
     * @param encrptyFile 加解密文件
     * @param encrptyedFile 加解密后的文件
     * @param mode 加密或解密模式
     * @param bufferSize 加解密缓冲大小，若bufferSize<=0,则为默认值：1024*1024
     */
    public  void encrptyFile(File encrptyFile, File encrptyedFile, int mode, int bufferSize){
        BufferedInputStream bis=null;
        CipherOutputStream cOutputStream=null;
        try{
            //得到加密模式
            Cipher cipher=getCipher(mode);
            bis=new BufferedInputStream(new FileInputStream(encrptyFile));
            cOutputStream=new CipherOutputStream(new FileOutputStream(encrptyedFile),cipher);
            // 默认读取文件，一次读1M
            int size = 1024 * 1024;
            if(bufferSize>0){
                size=bufferSize;
            }
            byte[] temp = new byte[size];
            int len = 0;
            while ((len = bis.read(temp)) > 0) {
                cOutputStream.write(temp,0,len);
                cOutputStream.flush();
            }
            cOutputStream.flush();
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(bis!=null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(cOutputStream!=null){
                try {
                    cOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取加解密模型
     * @param mode 加密或解密模式:Cipher.ENCRPTY 或 Cipher.DECRPTY
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidAlgorithmParameterException
     */
    public Cipher getCipher(int mode) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException {
        //生成随即码
        SecureRandom sr=new SecureRandom();
        //根据3DES加密密钥，生成密钥对象
        DESedeKeySpec dkSedeKeySpec=new DESedeKeySpec(new BigInteger(PRIVATE_CRYPT_KEY,16).toByteArray());
        //得到3DES密钥工厂
        SecretKeyFactory keyFactory= SecretKeyFactory.getInstance(ALGORITHM);
        //根据工厂，生成密钥对象
        SecretKey secretKey=keyFactory.generateSecret(dkSedeKeySpec);
        //得到加密模式
        Cipher cipher= Cipher.getInstance(CIPHER_MODEL);
        //得到3DES加密矢量
        IvParameterSpec iv = new IvParameterSpec(PRIVATE_IV.getBytes());
        //3DES初始化
        cipher.init(mode,secretKey,iv,sr);
        return cipher;
    }


    /**
     * 生成加密密钥,返回16进制字符串
     * @throws Exception
     */
    public static String generateKey() throws Exception {
        KeyGenerator kg= KeyGenerator.getInstance(ALGORITHM);
        kg.init(new SecureRandom());
        return new BigInteger(kg.generateKey().getEncoded()).toString(16);
    }

    /**
     * 文件加解密
     * @param baos
     * @param encrptyedFile 加解密后的文件
     * @param mode 加密或解密模式
     * @param bufferSize 加解密缓冲大小，若bufferSize<=0,则为默认值：1024*1024
     */
    public  void encrptyFile(ByteArrayOutputStream baos, File encrptyedFile, int mode, int bufferSize){
        CipherOutputStream cOutputStream=null;
        try{
            //得到加密模式
            Cipher cipher=getCipher(mode);
            cOutputStream=new CipherOutputStream(new FileOutputStream(encrptyedFile),cipher);
            // 默认读取文件，一次读1M
            cOutputStream.write(baos.toByteArray());
            cOutputStream.flush();
            cOutputStream.close();
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(cOutputStream!=null){
                try {
                    cOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
